Skip to contentMethod: static {...}
1: /*
2: * *********************************************************************************************************************
3: *
4: * blueMarine II: Semantic Media Centre
5: * http://tidalwave.it/projects/bluemarine2
6: *
7: * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/bluemarine2-src
23: * git clone https://github.com/tidalwave-it/bluemarine2-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.cec.impl;
28:
29: import javax.annotation.Nonnull;
30: import javax.inject.Inject;
31: import java.util.regex.Matcher;
32: import java.util.regex.Pattern;
33: import java.io.IOException;
34: import it.tidalwave.util.NotFoundException;
35: import it.tidalwave.util.ProcessExecutor;
36: import it.tidalwave.util.ProcessExecutor.ConsoleOutput;
37: import it.tidalwave.util.annotation.VisibleForTesting;
38: import it.tidalwave.util.spi.DefaultProcessExecutor;
39: import it.tidalwave.messagebus.MessageBus;
40: import it.tidalwave.messagebus.annotation.ListensTo;
41: import it.tidalwave.messagebus.annotation.SimpleMessageSubscriber;
42: import it.tidalwave.cec.CecEvent;
43: import it.tidalwave.cec.CecEvent.EventType;
44: import it.tidalwave.bluemarine2.message.PowerOnNotification;
45: import lombok.extern.slf4j.Slf4j;
46:
47: /***********************************************************************************************************************
48: *
49: * An adapter that receives notifications from {@code cec-client} and forwards events to the message bus.
50: *
51: * @stereotype Adapter
52: *
53: * @author Fabrizio Giudici
54: *
55: **********************************************************************************************************************/
56: @SimpleMessageSubscriber @Slf4j
57: public class CecClientAdapter
58: {
59: private static final String CEC_REGEX = "^TRAFFIC: *\\[ *([0-9]+)\\][ \\t>]*([0-9A-Fa-f]+):([0-9A-Fa-f]+):([0-9A-Fa-f]+)$";
60:
61: private static final Pattern CEC_PATTERN = Pattern.compile(CEC_REGEX);
62:
63: private ProcessExecutor executor;
64:
65: @Inject
66: private MessageBus messageBus;
67:
68: /*******************************************************************************************************************
69: *
70: * Parses the output from {@code cec-client} and fires events.
71: *
72: ******************************************************************************************************************/
73: private final ConsoleOutput.Listener listener = string ->
74: {
75: final Matcher matcher = CEC_PATTERN.matcher(string);
76:
77: if (matcher.matches())
78: {
79: final int eventType = Integer.parseInt(matcher.group(3), 16);
80: final int keyCode = Integer.parseInt(matcher.group(4), 16);
81:
82: try
83: {
84: final CecEvent event = EventType.forCode(eventType).createEvent(keyCode);
85: log.debug("Sending {}...", event);
86: messageBus.publish(event);
87: }
88: catch (NotFoundException e)
89: {
90: // 04:1a:01 - Give deck status
91: // 04:90:00 - Report power status
92: // 04:89:01 - Vendor specific data
93: log.warn("Not found: {} / {}", string, e.getMessage());
94: }
95: }
96: };
97:
98: /*******************************************************************************************************************
99: *
100: * At power on runs {@code cec-client}.
101: *
102: ******************************************************************************************************************/
103: @VisibleForTesting void onPowerOnReceived (@Nonnull @ListensTo final PowerOnNotification notification)
104: {
105: try
106: {
107: log.info("onPowerOnReceived({})", notification);
108: executor = DefaultProcessExecutor.forExecutable("/usr/bin/cec-client") // FIXME: path
109: .withArguments("-d", "8", "-t", "p", "-o", "blueMarine")
110: .start();
111: executor.getStdout().setListener(listener);
112: }
113: catch (IOException e)
114: {
115: log.error("Cannot run cec-client: {}", e.toString());
116: // TODO: UI notification of the error
117: }
118: }
119:
120: // TODO: kill executor on PowerOff?
121: }